package queryManager;

import componentManager.*;
import utils.*;

import general.IHM;

import java.io.*;
import java.util.*;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import componentManager.Terme;

import utils.InitDOMParser;


public class Requete {
	
	private int longueurMoyDoc;
	private HashMap<String,Double> keyWords;
	//la liste des noeuds pertinents pour chaque mot de la requete
	//<mot,liste des elements contenant le mot>
	private HashMap<String,ArrayList<NoeudResult>>  resultats;
	//<NomDoc+chemin(idElem),<mot,NoeudResult>>
	private HashMap<String,HashMap<String,NoeudResult>> outputs;
	//element,similarité
	private ArrayList<NoeudResult> similarityList;
	//<nomDoc,liste des noeud du doc qui ne sont pas de type p et qui contiennent des feuilles
	private ArrayList<NoeudResult> noeudATraite = new ArrayList<NoeudResult>();
	private HashMap<String,NoeudResult> dejaParcouru = new HashMap<String, NoeudResult>();
	private HashMap<String, Integer> nbreDocContenantTerme = new HashMap<String, Integer>();
	private Query query;
	private String outFilePutPath;
	
	public void initAttribute(Query q,HashMap<String,Double> keyWords,String outFilePutPath){
		this.outFilePutPath = outFilePutPath;
		this.query = q;
		this.longueurMoyDoc = BDD.getTermsNumber()/BDD.getDocsNumber();
		this.outputs = new HashMap<String, HashMap<String,NoeudResult>>();
		this.similarityList = new ArrayList<NoeudResult>();
		this.resultats = new HashMap<String, ArrayList<NoeudResult>>();
		
		//execution de la requete
//		System.out.println("----------------------");
		for(String s : keyWords.keySet()){
			if(!IHM.stopList.contains(s)){
//				System.out.println(s);
				getPertinentsNodesByWord(s);
			}
		}
//		System.out.println("----------------------");
		calculateTf();
		fillOutput();
		calculatesSimilarity();
		Collections.sort(this.similarityList, new Comparator<NoeudResult>() {
			@Override
			public int compare(NoeudResult o1, NoeudResult o2) {
				Double val1 = o1.getSimilarite();
				Double val2 = o2.getSimilarite();
				return val1.compareTo(val2);
			}
		});
		Collections.reverse(this.similarityList);
	}
	
	
	public Requete(Query q,HashMap<String,Double> keyW,String outFilePutPath){
		this.keyWords = keyW;
		initAttribute(q,this.keyWords,outFilePutPath);
	}
	
	
	public Requete(Query q,String outFilePutPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
		this.keyWords = initKeyWords(q.getText(),GenerateId.ponderationTermeInitial);
		initAttribute(q,this.keyWords,outFilePutPath);
	}
	
	
	public void fillOutput(){
		for(String s : this.resultats.keySet()){
			for(NoeudResult n : this.resultats.get(s)){
				String identifiant = n.getDoc().getNomDoc()+n.getPath();
				if(!this.outputs.containsKey(identifiant)){
					HashMap<String,NoeudResult> temp = new HashMap<String, NoeudResult>();
					temp.put(s, n);
					this.outputs.put(identifiant, temp);
				}else {
					this.outputs.get(identifiant).put(s, n);
				}
			}
		}
	}
	
	public void calculatesSimilarity(){
		double r = 0;
		for(String s : this.outputs.keySet()){
			r=0;
			for(String mot : this.outputs.get(s).keySet()){
				r += (this.outputs.get(s).get(mot).getTf()+this.outputs.get(s).get(mot).getIdf()+this.outputs.get(s).get(mot).getEnv())*this.keyWords.get(mot);
			}
			NoeudResult nr = (NoeudResult) this.outputs.get(s).values().toArray()[0];
			nr.setSimilarite(r);
			this.similarityList.add(nr);
		}
	}
	
	/**
	 * 
	 */
	public void calculateTf(){
		ArrayList<NoeudResult> liste = new ArrayList<NoeudResult>();
		for(String s : resultats.keySet()){
			for(NoeudResult n :resultats.get(s)){
				n.setTf(n.getFreq()/(n.getFreq()+0.5+1.5*(n.getDoc().getNbreTermes()/longueurMoyDoc)));
			}
		}
	}

	public double environmentOccurences(ArrayList<NoeudResult> liste){
		double result = 0;
		for(NoeudResult nr : liste){
			if(nr.getLabel().equalsIgnoreCase("TITRE") || nr.getLabel().equalsIgnoreCase("DESCRIPTION")
					|| nr.getLabel().equalsIgnoreCase("AUTEUR")){
				result=result+GenerateId.ponderationEnvPertinent;
			}
			if(nr.getLabel().equalsIgnoreCase("SOUS-TITRE")){
//				System.out.println("ici");
				result=result+GenerateId.ponderationEnvNonPertinent;
			}
		}
		return result;
	}
	
	public static HashMap<String, Double> initKeyWords(String ch,Double ponderation) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
		HashMap<String, Double> result = new HashMap<String, Double>();
		String[] words = ch.split(Parser.DELIMITERS);
		for(String s: words){
			if(!IHM.stopList.contains(s))
				result.put(Terme.lemmatise(s),ponderation);
		}
		return result;
	}
	
	/**
	 * Initialise les noeuds pertinent pour le terme spécifié
	 * et calcule les frequences pour chaque noeud
	 * @param word
	 */
	public void getPertinentsNodesByWord(String word){
		//<nomDocument,liste des noeuds contenant le terme>
		HashMap<String,ArrayList<NoeudResult>> temp = BDD.getOccurencesOfTerme(word);
		this.nbreDocContenantTerme.put(word,temp.keySet().size());
		
		for(String nomDoc: temp.keySet()){
			noeudATraite.clear();
			dejaParcouru.clear();
			double envOccur = environmentOccurences(temp.get(nomDoc));
			traitNodes(temp.get(nomDoc),word,envOccur);
		}
		//on calcule le TF de chaque noeud
		
	}
	
	
	
	/**
	 * Sépare les noeuds résultats p des noeuds item dont la frequence doit etre remonté
	 * jusqu'au paragraphe. Puis utilse la fonction remonterFreqNodes pour retrouver
	 * les paragraphes correspondants
	 * @param nodesList
	 */
	public void traitNodes(ArrayList<NoeudResult> nodesList,String word,double envOccur){
		for(NoeudResult nr : nodesList){
			nr.setIdf(this.nbreDocContenantTerme.get(word)/(double)BDD.getDocsNumber());
			nr.setEnv(envOccur);
			if(nr.getLabel().equalsIgnoreCase("p")){
//				if(!isPresent(resultats, nr))
				if(!resultats.containsKey(word)){
					ArrayList<NoeudResult> ar = new ArrayList<NoeudResult>();
					ar.add(nr);
					resultats.put(word,ar);
				}
				else{
					resultats.get(word).add(nr);
				}
					
			}else{
				Node n = getCorrespondantNode(nr.getDoc().getNomDoc(), nr.getPath());
				nr.setNode(n);
				if(n.getChildNodes().getLength()==1 && nr.getLabel().equalsIgnoreCase("ITEM")){
					noeudATraite.add(nr);
				}
			}
		}
		
		remonterFreqNodes(noeudATraite,word,envOccur);
	}
	
	/**
	 * Remonte les frequences de tous les noeuds d'un document pertinent 
	 * jusqu'a atteindre des paragraphes
	 * @param liste 
	 * @param word
	 */
	public void remonterFreqNodes(ArrayList<NoeudResult> liste,String word,double envOccur){

		if(noeudATraite.size()>0){
			boolean fini=true;
			for(NoeudResult nr : liste){
				if(!remonterFreqNode(nr,word,envOccur).equalsIgnoreCase("p"))
					fini=false;
			}
			if(!fini){
				ArrayList<NoeudResult> a = new ArrayList<NoeudResult>(this.dejaParcouru.values());
				remonterFreqNodes(a, word,envOccur);
			}
		}
	}
	
	private boolean isPresent(Set<NoeudResult> set, NoeudResult n){
		boolean result = false;
		for(NoeudResult no : set){
			if(no.getPath().equalsIgnoreCase(n.getPath()) && no.getDoc().getNomDoc().equalsIgnoreCase(n.getDoc().getNomDoc())){
				result = true;
				break;
			}
		}
		
		return result;
	}
	
	
	
	/**
	 * Cette fonction remonte la frequence d'un noeud vers le noeud parent
	 * si le noeud parent est un paragraphe il est ajoute aux resultats
	 * @param nr
	 * @param word
	 * @return
	 */
	public String remonterFreqNode(NoeudResult nr,String word,double envOccur){
		String result = "";
		if(nr.getNode().getParentNode()!=null){
			NoeudResult nres = new NoeudResult();
			nres.setNode(nr.getNode().getParentNode());
			nres.setEnv(envOccur);
//			Xpath
//			System.out.println("****************"+xmlnres.getNode().);
			nres.setDoc(nr.getDoc());
			nres.setIdf(this.nbreDocContenantTerme.get(word)/(double)BDD.getDocsNumber());
			nres.setPath(nr.getPath().substring(0, nr.getPath().length()-2));
			nres.setLabel(nr.getNode().getParentNode().getNodeName());
			//si on n a pas deja parcouru le pere du noeud specifié
			if( dejaParcouru.get(nres.getPath())==null){
				nres.setFreq(nr.getFreq());
				dejaParcouru.put(nres.getPath(),nres);
				if(nres.getLabel().equalsIgnoreCase("P")){	
					if(!resultats.containsKey(word)){
						ArrayList<NoeudResult> ar = new ArrayList<NoeudResult>();
						ar.add(nres);
						resultats.put(word,ar);
					}
					else{
						resultats.get(word).add(nres);
					}
				}
				result = nres.getLabel();
			}else{//
				NoeudResult dej = dejaParcouru.get(nres.getPath());
				dej.setFreq(nr.getFreq()+dej.getFreq());
				result = dej.getLabel();
			}
		}
		return result;
	}
	
	
	
	public NoeudResult findNoeudEquivalent(Node n, ArrayList<NoeudResult> nodeList){
		NoeudResult result=null;
		for(NoeudResult noeud : nodeList){
			if(noeud.getNode().equals(n)){
				result = noeud;
				break;
			}
		}
		return result;
	}
	
	
	
	
	public HashMap<String, ArrayList<NoeudResult>> getResultats() {
		return resultats;
	}

	public void setResultats(HashMap<String, ArrayList<NoeudResult>> resultats) {
		this.resultats = resultats;
	}

	//A modifier
	public String toString(){
		String result = "";
		for(NoeudResult s : this.similarityList){
//			result+=s.toString();
			result+="Collection/"+s.getDoc().getNomDoc()+"\t"+ getCorrespondantPath(s.getDoc().getNomDoc(), s.getPath())+" "+1+"\n";
		}
		return result; 
	}
	
	public  Node getCorrespondantNode(String fileName,String path){
		String[] positions = path.split("-");
		InitDOMParser init = new InitDOMParser();
		Document document = null;
		try {
			document = init.getConstructeur().parse(new File("Collection/"+fileName));
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		Element racine = document.getDocumentElement();
		Node node = racine;
		int courant=0;
		ArrayList<Node> children = new ArrayList<Node>();
		for(int i = 1;i<positions.length;i++){
			children.clear();
			courant = Integer.valueOf(positions[i]);
			NodeList l = node.getChildNodes();
			for(int j=0;j<l.getLength();j++){
				if(l.item(j).getNodeType()!=Node.TEXT_NODE ){
					children.add(l.item(j));
				}
			}
			node = children.get(courant-1);
		}
		return node;
	}
	
	
	public String getCorrespondantPath(String fileName,String path){
		String[] positions = path.split("-");
		InitDOMParser init = new InitDOMParser();
		Document document = null;
		try {
			document = init.getConstructeur().parse(new File("Collection/"+fileName));
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		Element racine = document.getDocumentElement();
		String res = "/"+racine.getNodeName()+"["+1+"]";
		Node node = racine;
		int courant=0;
		ArrayList<Node> children = new ArrayList<Node>();
		int nombre=0;
		for(int i = 1;i<positions.length;i++){
			children.clear();
			courant = Integer.valueOf(positions[i]);
			NodeList l = node.getChildNodes();
			for(int j=0;j<l.getLength();j++){
				if(l.item(j).getNodeType()!=Node.TEXT_NODE ){
					children.add(l.item(j));
				}
			}
			node = children.get(courant-1);
			for(int j=0;j<courant;j++){
				if(children.get(j).getNodeName().equalsIgnoreCase(node.getNodeName())){
					nombre++;
				}
			}
			res += "/"+node.getNodeName()+"["+nombre+"]";
			nombre=0;
		}
		return res;
	}
	
	/*
	 * Sauvegarde les resultats de la requete dans un ficher nommé "sortie_idRequete"
	 * idrequete étant l'id de la requete
	 */
	public void printInFile(){
		FileWriter fw  = null;
		try {
			fw = new FileWriter(new File("./"+outFilePutPath+"/qrel"+this.query.getId().substring(1)+".txt"));
			fw.write(this.toString());
			fw.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	
	

}
